/*
 * Copyright 2007 National Bioinformatics Network
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package dgbgui.gui;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Observable;
import java.util.Set;

import org.jgrapht.graph.DefaultEdge;

import dgbgui.gui.widgets.WidgetFactory;
import dgbgui.gui.widgets.WidgetStore;
import dgbgui.gui.widgets.visual.modeldriven.ModelDrivenConceptWidget;
import dgbgui.gui.widgets.visual.modeldriven.ModelGraphWidget;
import dgbgui.modeldocument.DGBModel;
import dgbgui.modeldocument.ModelDrivenWorkspace;
import dgbgui.modeldocument.ResultStore;
import dgbgui.modeldocument.primitives.Concept;
import dgbgui.selection.SelectionManager;

/**
 * @author bjnortier
 * The scene for the model-driven perspective
 */
public class ModelDrivenOutlineScene extends GraphicsScene {
    
    
    public void update(Observable source, Object data) {
        // Just set to changed so the view is notified of the selection
        setChanged();
        notifyObservers();
        
        if ( source instanceof ResultStore ) {
            ResultStore.ObservableData observedData = (ResultStore.ObservableData)data;
            switch( observedData.getType() ) {
            case QUERY_SUBMITTED:
            case RESULT_RECEIVED:
                clearScene();
                createNewScene();
                break;
            }
        }
        
        if ( source instanceof ModelDrivenWorkspace ) {
            
            ModelDrivenWorkspace.ObservableData observedData = (ModelDrivenWorkspace.ObservableData)data;
            switch ( observedData.getEvent() ) {
            case CLEARED: clearScene(); break;
            case RECREATED: createNewScene(); break;
            case UPDATED:
            case TARGET_SET:
            case INCLUDE_ADDED:
            case INCLUDE_REMOVED:
                clearScene();
                createNewScene(); 
                break;
            default: /* Do nothing */ break;
            }
        }
        
        if ( source instanceof SelectionManager ) {
            assert ( data instanceof SelectionManager.ObservableData );
            SelectionManager.ObservableData selectionData = (SelectionManager.ObservableData)data;
            LinkedHashSet<Concept> selectedConcepts = new LinkedHashSet<Concept>();

            for ( Object obj : selectionData.getItems() ) {
                if ( obj instanceof Concept ) {
                    selectedConcepts.add((Concept)obj);
                }
            }
            if ( selectedConcepts.size() > 0 ) {
                switch (selectionData.getType()) {
                case ADD : conceptsSelected(selectedConcepts); break;
                case REMOVE : conceptsDeselected(selectedConcepts); break;
                }
            }
        }
    }
    
    
    

    private void clearScene() {
        m_layer.removeAllChildren();
    }
    
    private void createNewScene() {
        
        DGBModel baseModel = ModelDrivenWorkspace.getInstance().getModel();
        if ( baseModel != null ) {
            m_graphWidget = WidgetFactory.createWidget(m_layer,baseModel);
        }
    }
    
    // ----- Selection -----
    
    private void conceptsSelected(Set<Concept> concepts) {
        restoreConnectedDisplay();
        
        for ( Concept concept : concepts ) {
            ModelDrivenConceptWidget w = WidgetStore.getInstance().getModelDrivenWidget(concept);
            if ( w != null ) {
                w.highlight();
            }
        }
        
        ModelDrivenWorkspace workspace = ModelDrivenWorkspace.getInstance();
        
        // If the target is the only concept selected
            //Concept target = workspace.getTarget();
        if ( concepts.size() == 1 ) {
            Concept currentConcept = concepts.iterator().next();
            if ( workspace.isInModel(currentConcept)) {
                m_sourceConcept = currentConcept;

                m_includableConcepts = workspace.getConnectedConcepts(m_sourceConcept);
                m_excludableConcepts = new HashSet<Concept>();

                // Remove the concepts that are already connected to the target
                Set<DefaultEdge> alreadyIncluded = workspace.getIncludeEdges();
                for ( DefaultEdge edge : alreadyIncluded ) {
                    // Add the source of the edge as excludable
                    if ( workspace.getIncludeTarget(edge) == currentConcept ) {
                        // NB concepts are only excludable if they are on the fringe (i.e. they
                        // do not include concepts themselves)
                        Concept source = workspace.getIncludeSource(edge);
                        if ( workspace.isOnFringe(source) ) {
                            m_excludableConcepts.add(source);
                        }
                    }

                    // Remove the already included source, (it's connected, but 
                    // alredy included)
                    m_includableConcepts.remove(workspace.getIncludeSource(edge));
                    m_includableConcepts.remove(workspace.getIncludeTarget(edge));

                }
                
                //System.out.println("Includable:" + m_includableConcepts);
                //System.out.println("Excludable:" + m_excludableConcepts);
                    
                // Show the includable and excludable terms
                m_graphWidget.showIncludable(m_sourceConcept,m_includableConcepts);
                m_graphWidget.showExcludable(m_sourceConcept,m_excludableConcepts);
            }
        }
    }

    /**
     * Deselect the concepts
     * @param concepts
     */
    private void conceptsDeselected(Set<Concept> concepts) {
        restoreConnectedDisplay();
        
        for ( Concept concept : concepts ) {
            ModelDrivenConceptWidget w = WidgetStore.getInstance().getModelDrivenWidget(concept);
            if ( w != null ) {
                w.unhighlight();            
            }
        }
    }
    
    /**
     * Restore the display of terms that can be included/excluded
     */
    private void restoreConnectedDisplay() {
        if ( m_sourceConcept != null ) {
            if ( m_includableConcepts != null ) {
                m_graphWidget.restoreIncludable(m_sourceConcept,m_includableConcepts);
            }
            if ( m_excludableConcepts != null ) {
                m_graphWidget.restoreIncludable(m_sourceConcept,m_excludableConcepts);
            }
        }
        m_sourceConcept = null;
        m_includableConcepts = null;
        m_excludableConcepts = null;
    }
    
    // ---------- Singleton ----------

    
    private ModelDrivenOutlineScene() {
        m_instance = this;
    }
    
    public static ModelDrivenOutlineScene getInstance() {
        if ( m_instance == null ) {
            m_instance = new ModelDrivenOutlineScene();
        }
        return m_instance;
    }
    
    private static ModelDrivenOutlineScene m_instance;
    
    // ---------- Fields ----------
    
    // The current model graph widget
    ModelGraphWidget m_graphWidget = null;
    Concept m_sourceConcept = null;
    Set<Concept> m_includableConcepts = null;
    Set<Concept> m_excludableConcepts = null;



}
